/**
  * brief 程序比较小，将接口层和控制层合到一起；主要做一些任务分发，状态监控等一些简单工作；复杂的耗时的逻辑放到任务中moveToThread中去执行
  */

#include <QTimer>
#include <QDateTime>
#include <QDBusArgument>
#include <QDBusMessage>
#include <QDBusPendingReply>
#include <QDBusMessage>
#include <QDBusUnixFileDescriptor>
#include <QDBusConnection>
#include <mutex>
#include <sys/reboot.h>
#include "mybackupmanager.h"
#include "../common/utils.h"
#include "mymountproxy.h"
#include "workerfactory.h"
#include "parsebackuplist.h"

/**
 * @brief 构造函数
 */
MyBackupManager::MyBackupManager()
{
    // 注册BackupWrapper类型，之后qdbus接口才能使用
    BackupWrapper::registerMetaType();
}

/**
 * @brief 析构函数
 */
MyBackupManager::~MyBackupManager()
{
    if (workerThread.isRunning()) {
        workerThread.quit();
        workerThread.wait();
    }
}

/**
 * @brief 挂载backup分区，兼容提供给自动更新模块的老接口
 * @return
 */
int MyBackupManager::Mount_backup_partition()
{
    m_needUmount = false;
    MyMountProxy mymount;
    return int(mymount.mountBackupPartition());
}

/**
 * @brief 卸载backup分区
 * @return
 */
bool MyBackupManager::umountBackupPartition()
{
    m_needUmount = true;
    return umountBackup();
}

/**
 * @brief 卸载backup分区
 * @return
 */
bool MyBackupManager::umountBackup()
{
    // /backup分区卸载
    if (m_needUmount && m_isActive) {
        QTimer::singleShot(5*1000, this, &MyBackupManager::umountBackup);
        return true;
    } else if (m_needUmount) {
        MyMountProxy mymount;
        return mymount.umountBackupPartition();
    }

    return true;
}

/**
 * @brief 环境检测
 * @param backupWrapper，参数
 * @return 0，正确启动；非0，出现错误
 */
int MyBackupManager::checkEnv(const BackupWrapper& backupWrapper)
{
    qDebug("MyBackupManager::checkEnv invoke begin");
    if (m_isActive || !lock(backupWrapper.m_frontUid)) {
        emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
        return int(BackupResult::LOCK_PROGRAM_FAIL);
    }

    Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
    if (nullptr == worker) {
        emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
        return int(BackupResult::NO_FOUND_DEALCLASS);
    }

    worker->setParam(backupWrapper);
    connect(worker, &Worker::checkResult, this, [&] (int result) {
        emit this->sendEnvCheckResult(result);
        this->finished();
    });
    worker->moveToThread(&workerThread);
    connect(&workerThread, &MyThread::started, worker, &Worker::checkEnv);
    connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);

    workerThread.start();

    qDebug("MyBackupManager::checkEnv invoke end");
    return int(BackupResult::BACKUP_RESULT_INIT);
}

/**
 * @brief 备份
 * @param backupWrapper
 * @return 0，正确启动备份；非0，出现错误
 */
int MyBackupManager::goBackup(const BackupWrapper& backupWrapper)
{
    qDebug("MyBackupManager::goBackup invoke begin");
    if (m_isActive || !lock(backupWrapper.m_frontUid)) {
        emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
        return int(BackupResult::LOCK_PROGRAM_FAIL);
    }

    Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
    if (nullptr == worker) {
        emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
        return int(BackupResult::NO_FOUND_DEALCLASS);
    }

    worker->setParam(backupWrapper);
    connect(worker, &Worker::checkResult, this, [&](int result) {
        emit this->sendEnvCheckResult(result);

        switch (result) {
        case int(BackupResult::CHECK_ENV_SUCCESS) :
        case int(BackupResult::MKSQUASHFS_START_SUCCESS) :
        case int(BackupResult::BACKUP_START_SUCCESS) :
        case int(BackupResult::START_CANCEL) :
            break;
        default:
            this->finished();
            break;
        }
    });
    connect(worker, &Worker::progress, this, [&](int rate) {
        emit this->progress(int(BackupState::WORKING), rate);
    });
    connect(worker, &Worker::workResult, this, [&] (bool result) {
        emit this->backupFinished(result);
        this->finished();
    });
    worker->moveToThread(&workerThread);
    connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
    connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);
    connect(&workerThread, &MyThread::cancelWork, worker, &Worker::cancel);

    workerThread.start();

    qDebug("MyBackupManager::goBackup invoke end");
    return int(BackupResult::BACKUP_RESULT_INIT);
}

/**
 * @brief 还原
 * @param backupWrapper
 * @return
 */
int MyBackupManager::goRestore(const BackupWrapper& backupWrapper)
{
    qDebug("MyBackupManager::goRestore invoke begin");
    if (m_isActive || !lock(backupWrapper.m_frontUid)) {
        emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
        return int(BackupResult::LOCK_PROGRAM_FAIL);
    }

    Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
    if (nullptr == worker) {
        emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
        return int(BackupResult::NO_FOUND_DEALCLASS);
    }

    worker->setParam(backupWrapper);
    connect(worker, &Worker::checkResult, this, [&](int result) {
        emit this->sendEnvCheckResult(result);

        switch (result) {
        case int(BackupResult::CHECK_ENV_SUCCESS) :
        case int(BackupResult::RESTORE_START_SUCCESS) :
            break;
        default:
            this->finished();
            break;
        }
    });
    connect(worker, &Worker::progress, this, [&](int rate) {
        emit this->progress(int(BackupState::WORKING), rate);
    });
    connect(worker, &Worker::workResult, this, [&] (bool result) {
        emit this->sendRestoreResult(result);
        this->finished();
    });
    worker->moveToThread(&workerThread);
    connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
    connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);

    workerThread.start();

    qDebug("MyBackupManager::goRestore invoke end");
    return int(BackupResult::BACKUP_RESULT_INIT);
}

/**
 * @brief 删除备份
 * @param backupWrapper
 * @return
 */
int MyBackupManager::deleteBackupPoint(const BackupWrapper& backupWrapper)
{
    qDebug("MyBackupManager::deleteBackupPoint invoke begin");
    if (m_isActive || !lock(backupWrapper.m_frontUid)) {
        emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
        return int(BackupResult::LOCK_PROGRAM_FAIL);
    }

    Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
    if (nullptr == worker) {
        emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
        return int(BackupResult::NO_FOUND_DEALCLASS);
    }

    worker->setParam(backupWrapper);
    connect(worker, &Worker::workResult, this, [&] (bool result) {
        emit this->sendDeleteResult(result);
        this->finished();
    });
    worker->moveToThread(&workerThread);
    connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
    connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);

    workerThread.start();

    qDebug("MyBackupManager::deleteBackupPoint invoke end");
    return int(BackupResult::BACKUP_RESULT_INIT);
}

/**
 * @brief ghost镜像
 * @param backupWrapper
 * @return
 */
int MyBackupManager::ghostBackup(const BackupWrapper& backupWrapper)
{
    qDebug("MyBackupManager::ghostBackup invoke begin");
    if (m_isActive || !lock(backupWrapper.m_frontUid)) {
        emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
        return int(BackupResult::LOCK_PROGRAM_FAIL);
    }

    Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
    if (nullptr == worker) {
        emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
        return int(BackupResult::NO_FOUND_DEALCLASS);
    }

    worker->setParam(backupWrapper);
    connect(worker, &Worker::checkResult, this, [&](int result) {
        emit this->sendEnvCheckResult(result);

        switch (result) {
        case int(BackupResult::CHECK_ENV_SUCCESS) :
        case int(BackupResult::GHOST_START_SUCCESS) :
        case int(BackupResult::MKSQUASHFS_DO_SUCCESS) :
        case int(BackupResult::START_CANCEL) :
            break;
        default:
            this->finished();
            break;
        }
    });
    connect(worker, &Worker::progress, this, [&](int rate) {
        emit this->progress(int(BackupState::WORKING), rate);
    });
    connect(worker, &Worker::workResult, this, [&] (bool result) {
        emit this->sendGhostBackupResult(result);
        this->finished();
    });
    worker->moveToThread(&workerThread);
    connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
    connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);
    connect(&workerThread, &MyThread::cancelWork, worker, &Worker::cancel);

    workerThread.start();

    qDebug("MyBackupManager::ghostBackup invoke end");
    return int(BackupResult::BACKUP_RESULT_INIT);
}

/**
 * @brief 控制面板调用的备份接口，重构暂时先兼容以前的老接口
 * @param autobackup_name 备份名称
 * @param create_note 创建时备注
 * @param inc_note  增量备注
 * @param frontUserName 前端用户名
 * @param frontUid 前端用户id
 */
void MyBackupManager::autoBackUpForSystemUpdate_noreturn(const QString& autobackup_name, const QString& create_note, const QString& inc_note, const QString& frontUserName, int frontUid)
{
    qDebug("MyBackupManager::autoBackUpForSystemUpdate_noreturn invoke begin");

    if (m_isActive || !lock(frontUid)) {
        emit sendStartBackupResult(int(BackupResult::LOCK_PROGRAM_FAIL));
        return ;
    }

    BackupWrapper backupWrapper;
    backupWrapper.m_uuid = AUTO_BACKUP_UUID;
    QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
    xmlPath.replace("//", "/");
    ParseBackupList parseXml(xmlPath);
    ParseBackupList::BackupPoint backupPoint = parseXml.findBackupPointByUuid(backupWrapper.m_uuid);
    if (autobackup_name.isEmpty()) {
        if (backupPoint.m_backupName.isEmpty())
            backupWrapper.m_backupName = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss");
        else
            backupWrapper.m_backupName = backupPoint.m_backupName;
    } else {
        backupWrapper.m_backupName = autobackup_name;
    }
    backupWrapper.m_backupPaths << "/";
    backupWrapper.m_backupExcludePaths = Utils::getFromExcludePathsFile();
    backupWrapper.m_backupExcludePaths << "/home";
    backupWrapper.m_backupExcludePaths << "/root";
    backupWrapper.m_backupExcludePaths << "/data/home";
    backupWrapper.m_backupExcludePaths << "/data/root";
    backupWrapper.m_type = BackupType::BACKUP_SYSTEM;
    backupWrapper.m_iPosition = BackupPosition::LOCAL;
    backupWrapper.m_frontUserName = frontUserName;
    backupWrapper.m_frontUid = frontUid;
    backupWrapper.m_note = create_note.isEmpty() ? inc_note : create_note;

    Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
    if (nullptr == worker) {
        emit sendStartBackupResult(int(BackupResult::NO_FOUND_DEALCLASS));
        return ;
    }

    worker->setParam(backupWrapper);
    connect(worker, &Worker::checkResult, this, [&](int result) {
        switch (result) {
        case int(BackupResult::CHECK_ENV_SUCCESS) :
        case int(BackupResult::MKSQUASHFS_START_SUCCESS) :
        case int(BackupResult::BACKUP_START_SUCCESS) :
            break;
        default:
            emit this->sendStartBackupResult(result);
            this->finished();
            if (!Utils::isRunning("kybackup")) {
                this->umountBackupPartition();
            }
            break;
        }
    });
    connect(worker, &Worker::progress, this, [&](int rate) {
        emit this->sendRate(int(BackupState::WORKING), rate);
    });
    connect(worker, &Worker::workResult, this, [&] (bool result) {
        emit this->sendBackupResult(result);
        this->finished();
        if (!Utils::isRunning("kybackup")) {
            this->umountBackupPartition();
        }
    });
    worker->moveToThread(&workerThread);
    connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
    connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);

    workerThread.start();

    qDebug("MyBackupManager::autoBackUpForSystemUpdate_noreturn invoke end");
}

/**
 * @brief 控制面板调用的获取系统备份接口
 * @param state
 * @return
 */
QString MyBackupManager::getBackupCommentForSystemUpdate(QString& state)
{
    QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
    xmlPath.replace("//", "/");
    ParseBackupList parseXml(xmlPath);
    ParseBackupList::BackupPoint backupPoint = parseXml.findBackupPointByUuid(AUTO_BACKUP_UUID);
    state = backupPoint.m_state;
    return backupPoint.m_backupName;
}

/**
 * @brief 获取备份状态
 * @param isActive，是否活动状态
 * @return
 */
int MyBackupManager::getBackupState(bool& isActive)
{
    isActive = m_isActive;
    return int(m_backupState);
}

/**
 * @brief 取消操作
 */
int MyBackupManager::cancel()
{
    if (workerThread.isRunning())
        emit workerThread.cancelWork();
    else
        return 1;

    return 0;
}

/**
 * @brief 重启操作
 * @return
 */
int MyBackupManager::reboot()
{
    return ::reboot(RB_AUTOBOOT);;
}

/**
 * @brief 任务结束
 */
void MyBackupManager::finished()
{
    if (workerThread.isRunning()) {
        workerThread.quit();
        workerThread.wait();
    }

    unlock();
}

/**
 * @brief 锁定应用
 * @param frontUid，锁定应用的用户id
 * @return bool
 */
bool MyBackupManager::lock(int frontUid)
{
    std::lock_guard<spinlock_mutex> lock(m_mutex);

    int lock_file_fd = Utils::lockProgram(frontUid);
    if (lock_file_fd < 0) {
        Utils::rmLockFile();
        return false;
    }

    // 用于替换上面的文件锁的，对应需求8688
//    if (!inhibit())
//        return false;
    inhibit();

    m_fdLockFile = lock_file_fd;
    m_backupState = BackupState::PREPARING;
    m_isActive = true;

    setKysecStatus(false);

    return true;
}

/**
 * @brief 阻止session关机用dbus新接口Inhibit来替换锁文件的方式
 * Inhibti函数有四个参数，按顺序分别为：
        what:需要阻止的是哪些过程，如关机、重启；可以输入的值有：”shutdown”(会同时阻止关机重启)，”sleep”（会同时阻止睡眠休眠），需要同时阻止关机和睡眠可以输入 ”shutdown:sleep”
        who:是谁要阻止，调用该接口的程序名，如”ukui-sessions”
        why：阻止的原因，简单描述即可，如”package installing”
        mode:阻止的方式；可输入的值有”block”(无限期阻止，等到主动取消阻止才会取消)，”delay”（超时阻止，超时一段时间后不管阻止锁，直接执行）
        建议使用”block”的方式阻止，加锁后记得解锁
        可以使用同服务下ListInhibitors方法获取当前的inhibit锁，测试验证是否成功注册
 * @return
 */
bool MyBackupManager::inhibit()
{
    QDBusMessage message;
    message = QDBusMessage::createMethodCall("org.freedesktop.login1",
                                             "/org/freedesktop/login1",
                                             "org.freedesktop.login1.Manager",
                                             QStringLiteral("Inhibit"));
    QString why("The backup and restore tool is busy, please wait.");
    // 区分中英文
    QString locale = QLocale::system().name();
    if (locale == "zh_CN") {
        why = QString("备份还原工具正在忙碌，请稍等");
    } else if (locale == "bo_CN") {
        why = QString("གྲབས་ཉར་སོར་ཆུད་ལག་ཆ་བྲེལ་བཞིན་ཡོད་།ཏོག་ཙམ་སྒུག་རོགས་།");
    }

    // QVariantList({what, who, why, mode})
    QVariantList args;
    args << QString("shutdown:sleep");
    args << QString("backup-daemon");
    args << why;
    args << QString("block");
    message.setArguments(args);

    QDBusPendingReply<QDBusUnixFileDescriptor> reply = QDBusConnection::systemBus().call(message);

    if (!reply.isValid()) {
        qDebug() << "inhibit faile!";
        return false;
    }
    QDBusUnixFileDescriptor inhibitFileDescriptor = reply.value();
    inhibitFileDescriptor.swap(m_inhibitFileDescriptor);
    qDebug() << "inhibit success!";
    return true;
}

void MyBackupManager::uninhibit()
{
    if (!m_inhibitFileDescriptor.isValid()) {
        return;
    }

    qDebug() << "uninhibit success!";
    m_inhibitFileDescriptor = QDBusUnixFileDescriptor();
}

/**
 * @brief 解锁应用
 * @return bool
 */
bool MyBackupManager::unlock()
{
    std::lock_guard<spinlock_mutex> lock(m_mutex);

    setKysecStatus(true);

    Utils::unLockProgram(m_fdLockFile);
    uninhibit();

    m_fdLockFile = -1;
    m_backupState = BackupState::BACKUP_STATE_INIT;
    m_isActive = false;

    return true;
}

/**
 * @brief 设置安全状态
 * @param status,true-开启；false-关闭
 * @note status=true时不一定会开启，取决于关闭时安全状态;
 *       使用此方法时注意：一定要先setKysecStatus(false);才能setKysecStatus(true);
 *       使用场景：备份/还原等业务时先关闭安全保护，业务完成后再重启安全保护
 */
void MyBackupManager::setKysecStatus(bool status)
{
    // openkylin平台中不开源安全模块，删掉安全模块的调用
    Q_UNUSED(status)
}
